/*
 * 基于muduo网络库和Protobuf的RPC客户端实现
 * 功能：支持加法运算和英文翻译的RPC调用
 */

#include "muduo/proto/dispatcher.h"    // Protobuf消息分发器
#include "muduo/proto/codec.h"        // Protobuf编解码器
#include "muduo/base/Logging.h"        // 日志模块
#include "muduo/base/Mutex.h"          // 互斥锁
#include "muduo/net/EventLoop.h"       // 事件循环
#include "muduo/net/TcpClient.h"       // TCP客户端
#include "muduo/net/EventLoopThread.h" // 事件循环线程
#include "muduo/base/CountDownLatch.h" // 同步计数器

#include "request.pb.h"                // Protobuf生成的请求/响应消息头文件
#include <iostream>

// RPC客户端类
class Client 
{
public:
    // 定义Protobuf消息智能指针类型别名
    typedef std::shared_ptr<google::protobuf::Message> MessagePtr;
    typedef std::shared_ptr<bit::AddResponse> AddResponsePtr;
    typedef std::shared_ptr<bit::TranslateResponse> TranslateResponsePtr;

    // 构造函数: 初始化客户端并连接服务器
    Client(const std::string &sip, int sport):
        _latch(1),  // 同步计数器初始为1，用于等待连接建立
        // 创建TCP客户端，使用独立事件循环线程
        _client(_loopthread.startLoop(), muduo::net::InetAddress(sip, sport), "Client"),
        // 初始化消息分发器，绑定未知消息处理回调
        _dispatcher(std::bind(&Client::onUnknownMessage, this, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),
        // 初始化编解码器，绑定分发器的消息处理入口
        _codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)) 
    {
        // 注册具体消息类型的处理回调
        _dispatcher.registerMessageCallback<bit::TranslateResponse>(
            std::bind(&Client::onTranslate, this, 
                std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        
        _dispatcher.registerMessageCallback<bit::AddResponse>(
            std::bind(&Client::onAdd, this, 
                std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

        // 设置TCP客户端回调
        _client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec,
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        _client.setConnectionCallback(std::bind(&Client::onConnection, this, 
            std::placeholders::_1));      
    }

    // 连接服务器（阻塞直到连接成功）
    void connect() 
    {
        _client.connect();
        _latch.wait();  // 通过计数器实现同步等待连接建立
    }

    
    // 发送翻译请求
    void Translate(const std::string &msg) 
    {
        bit::TranslateRequest req;
        req.set_msg(msg);
        send(&req);
    }

    // 发送加法请求
    void Add(int num1, int num2) 
    {
        bit::AddRequest req;
        req.set_num1(num1);
        req.set_num2(num2);
        send(&req);
    }

private:
    // 发送Protobuf消息到服务器
    bool send(const google::protobuf::Message *message) 
    {
        if (_conn->connected())      // 检查连接状态
        {
            _codec.send(_conn, *message);  // 通过编解码器发送消息
            return true;
        }
        return false;
    }  

    // 翻译响应处理回调
    void onTranslate(const muduo::net::TcpConnectionPtr& conn, 
                    const TranslateResponsePtr& message, 
                    muduo::Timestamp) {
        std::cout << "翻译结果：" << message->msg() << std::endl;
    }

    // 加法响应处理回调
    void onAdd(const muduo::net::TcpConnectionPtr& conn, 
             const AddResponsePtr& message, 
             muduo::Timestamp) {
        std::cout << "加法结果：" << message->result() << std::endl;
    }

    // 未知消息处理回调
    void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn, 
                        const MessagePtr& message, 
                        muduo::Timestamp) {
        LOG_INFO << "收到未知消息类型: " << message->GetTypeName();
        conn->shutdown();  // 关闭连接
    }

    // 连接状态变化回调
    void onConnection(const muduo::net::TcpConnectionPtr& conn) 
    {
        if (conn->connected())    // 连接建立成功
        {
            _latch.countDown();   // 计数器减一，唤醒主线程
            _conn = conn;         // 保存连接对象
        } 
        else                      // 连接断开
        {
            _conn.reset();        // 释放连接对象
        }
    }

private:

    muduo::CountDownLatch _latch;             // 同步工具，用于等待连接建立       
    muduo::net::EventLoopThread _loopthread;  // 独立事件循环线程（处理网络IO）                          
    muduo::net::TcpConnectionPtr _conn;       // TCP连接对象指针   
    muduo::net::TcpClient _client;            // TCP客户端实例 
    ProtobufDispatcher _dispatcher;           // Protobuf消息分发器（路由消息到对应处理函数） 
    ProtobufCodec _codec;                     // Protobuf编解码器（网络字节流与消息对象转换）  
};

int main() 
{
    // 创建客户端实例，连接本地服务器8888端口
    Client client("127.0.0.1", 8888);
    client.connect();  // 阻塞直到连接成功

    // 发送翻译请求
    client.Translate("hello");
    
    // 发送加法请求
    client.Add(11, 22);

    // 等待响应返回（实际生产环境应使用更可靠的等待方式）
    sleep(1);
    return 0;
}